<template>
  <div class="shard-and-replica">
    <div class="sr-header">
      <t-input-group>
        <t-input v-model="keyword" style="width: 350px" allow-clear>
          <template #suffix>
            <icon-search />
          </template>
        </t-input>
        <t-select v-model="order" placeholder="索引排序" style="width: 120px; margin-left: 7px">
          <t-option :value="OrderType.NAME_ASC">名称正序</t-option>
          <t-option :value="OrderType.NAME_DESC">名称倒序</t-option>
          <t-option :value="OrderType.SIZE_ASC">大小正序</t-option>
          <t-option :value="OrderType.SIZE_DESC">大小倒序</t-option>
          <t-option :value="OrderType.DOCS_ASC">文档正序</t-option>
          <t-option :value="OrderType.DOCS_DESC">文档倒序</t-option>
        </t-select>
      </t-input-group>
    </div>
    <div class="sr-container">
      <!-- 标题 -->
      <div class="index">
        <div class="info">
          <div class="name"></div>
        </div>
        <div class="shards">
          <div v-for="node in nodeKeys" :key="node" class="shard-title">
            <t-typography-paragraph class="icon">
              <t-tooltip v-if="node === masterNode" content="主节点">
                <icon-star-fill style="color: var(--td-brand-color)" />
              </t-tooltip>
              <t-tooltip v-else-if="node === UNASSIGNED" content="未分配的节点">
                <icon-exclamation-circle-fill style="color: var(--td-error-color)" />
              </t-tooltip>
              <!-- 标准节点 -->
              <t-tooltip v-else content="工作节点">
                <icon-info-circle-fill />
              </t-tooltip>
            </t-typography-paragraph>
            <t-typography-paragraph bold>
              <t-button v-if="node === UNASSIGNED" variant="text" theme="primary">
                {{ nodes[node] ? nodes[node].name : node }}
              </t-button>
              <t-dropdown v-else position="br" type="text" :title="node">
                {{ nodes[node] ? nodes[node].name : node }}
                <icon-down />
                <t-dropdown-menu>
                  <t-dropdown-item @click="showClusterNode(node)">集群节点信息</t-dropdown-item>
                  <t-dropdown-item @click="showNode(node)">节点信息</t-dropdown-item>
                </t-dropdown-menu>
              </t-dropdown>
            </t-typography-paragraph>
          </div>
        </div>
      </div>
      <!-- 每一个索引 -->
      <div v-for="index in items" :key="index.name" class="index">
        <div class="info">
          <div class="name">
            <t-link class="link" @click="openIndexInfo(index.name)">{{ index.name }}</t-link>
          </div>
          <div class="size">size: {{ index.size }}</div>
          <div class="doc_count">docs: {{ index.docCount }}</div>
        </div>
        <!-- 每一个副本 -->
        <div class="shards">
          <div v-for="nodeKey in nodeKeys" :key="nodeKey" class="shard">
            <div
              v-for="(shard, idx) in index.nodes[nodeKey]"
              :key="shard ? shard.shard : idx"
              class="item"
              :class="handlerReplicaClass(shard)"
              @click="openJsonDialog(shard)"
            >
              {{ shard ? shard.shard : idx }}
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script lang="ts" setup>
import { useIndexStore, useUrlStore } from "@/store";
import { showJson, showJsonDialogByAsync } from "@/utils/model/DialogUtil";
import { Shard } from "@/components/es/domain/ClusterState";
import { useFuse } from "@vueuse/integrations/useFuse";
import { useIndexManageEvent } from "@/global/BeanFactory";
import { OrderType } from "@/store/components/HomeStore";
import { ClusterNode } from "$/elasticsearch-client";
import { extractByJsonPath } from "$/util";
import { clientInstanceGetStateApi } from "@/api";
import MessageUtil from "@/utils/model/MessageUtil";

const UNASSIGNED = "Unassigned";

interface IndexItem {
  name: string;
  size: string;
  docCount: string;
  // 节点ID => 节点内全部的分片
  nodes: Record<string, Array<Shard | null>>;
}

const keyword = ref("");
const order = ref(useIndexStore().order);
const hasUnAssigned = ref(false);
// 节点信息
const nodeKeys = computed<Array<string>>(() => {
  let keys = Object.keys(useIndexStore().nodes);
  if (hasUnAssigned.value) {
    keys.push(UNASSIGNED);
  }
  return keys;
});
const nodes = computed<Record<string, ClusterNode>>(() => useIndexStore().nodes);
const masterNode = computed(() => useIndexStore().masterNode);
// 索引信息
const indices = computed(() => {
  hasUnAssigned.value = false;
  const items = new Array<IndexItem>();
  useIndexStore().indices.forEach((index) => {
    const item: IndexItem = {
      name: index.name,
      size: index.size,
      docCount: index.doc_count,
      nodes: {}
    };
    for (let shardsKey in index.shards) {
      // shardsKey: 分片编号
      let shards = index.shards[shardsKey];
      for (let shard of shards) {
        if (!shard.node) {
          hasUnAssigned.value = true;
        }
        const key = shard.node || UNASSIGNED;
        let temp = item.nodes[key];
        if (!temp) {
          item.nodes[key] = new Array<Shard | null>();
          temp = item.nodes[key];
        }
        temp.push(shard);
      }
    }
    items.push(item);
  });
  return items;
});

const { results } = useFuse(keyword, indices, {
  matchAllWhenSearchEmpty: true,
  fuseOptions: {
    keys: [
      {
        name: "name"
      }
    ]
  }
});
const items = computed(() => results.value.map((e) => e.item));

watch(order, (value) => useIndexStore().sort(value));

function openJsonDialog(shard: Shard | null) {
  if (shard) {
    showJson(`${shard.index}/${shard.node}[${shard.shard}]`, JSON.stringify(shard, null, 2), {
      width: "600px"
    });
  }
}

function openIndexInfo(name: string) {
  useIndexManageEvent.emit(name);
}

function handlerReplicaClass(shard: Shard | null): string {
  if (!shard) {
    return "empty";
  }
  if (shard.state === "UNASSIGNED") {
    return "unassigned";
  }
  if (shard.primary) {
    return "primary";
  }
  return "";
}

function showClusterNode(node: string) {
  const { id } = useUrlStore();
  if (!id) return MessageUtil.error("请选择实例");
  showJsonDialogByAsync(
    `集群节点信息【${node}】`,
    clientInstanceGetStateApi(id, "/_nodes").then(
      (e) => extractByJsonPath(e, `$.nodes.${node}`) || ""
    ),
    {
      width: "600px"
    }
  );
}

function showNode(node: string) {
  const { id } = useUrlStore();
  if (!id) return MessageUtil.error("请选择实例");
  showJsonDialogByAsync(
    `节点信息【${node}】`,
    clientInstanceGetStateApi(id, "/_nodes/stats").then(
      (e) => extractByJsonPath(e, `$.nodes.${node}`) || ""
    ),
    {
      width: "600px"
    }
  );
}
</script>
<style scoped lang="less">
@import url(index.less);
</style>
